  # coding=utf-8

import os
import platform
#import tkinter
#from tkinter import *
from HCNetSDK import *
from PlayCtrl import *
from time import sleep
from datetime import datetime, timedelta
from ctypes import *
from openpyxl import load_workbook, Workbook
from concurrent.futures import ThreadPoolExecutor, as_completed
#import time
import calendar
from loguru import logger
from pathlib import Path
from pytz import timezone
from apscheduler.schedulers.background import BlockingScheduler
import time

'''
    @作者：潍坊 傅永
    @版本:2025-03-03 10:00
    @功能：



'''

log_dir = 'log'
levelinfo = 'INFO'
leveldebug = 'DEBUG'
logfilename = datetime.now().strftime("%Y%m%d")
logger.add(Path(log_dir).joinpath(f"{logfilename}.log"), rotation="10MB", encoding="utf-8", enqueue=True,
           retention="10 days", level=levelinfo)


WINDOWS_FLAG = True
#win = None  # 预览窗口
#funcRealDataCallBack_V30 = None  # 实时预览回调函数，需要定义为全局的

#PlayCtrl_Port = c_long(-1)  # 播放句柄
##Playctrldll = None  # 播放库
FuncDecCB = None   # 播放库解码回调函数，需要定义为全局的
NAME_LEN = 32       # 用户名最大长度
PASSWD_LEN = 16     # 密码最大长度
MAX_DOMAIN_NAME = 64# 域名最大长度
DEV_ID_LEN = 48     # 设备ID长度
# 定义常量
STREAM_ID_LEN = 32  # 流ID长度
# 通道基数定义（需根据SDK头文件确认）
MAX_CHANNUM_V30 = 64  # 典型值为64，对应256通道（64基础+192扩展）
DW_COMMAND = 6126  # 配置命令

class NET_DVR_TIME(ctypes.Structure):
    _fields_ = [
        ("dwYear", ctypes.c_uint32),
        ("dwMonth", ctypes.c_uint32),
        ("dwDay", ctypes.c_uint32),
        ("dwHour", ctypes.c_uint32),
        ("dwMinute", ctypes.c_uint32),
        ("dwSecond", ctypes.c_uint32),
    ]

class NET_DVR_RECORD_TIME_SPAN_INQUIRY(ctypes.Structure):
    _fields_ = [
        ("dwSize", ctypes.c_uint32),
        ("byType", ctypes.c_uint8),
        ("byRes", ctypes.c_uint8 * 63),
    ]

class NET_DVR_RECORD_TIME_SPAN(ctypes.Structure):
    _fields_ = [
        ("dwSize", ctypes.c_uint32),
        ("strBeginTime", NET_DVR_TIME),
        ("strEndTime", NET_DVR_TIME),
        ("byType", ctypes.c_uint8),
        ("byRes", ctypes.c_uint8 * 35),
    ]


# 定义NET_DVR_STREAM_INFO结构体
class NET_DVR_STREAM_INFO(Structure):
    _fields_ = [
        ("dwSize", c_uint32),  # 结构体大小
        ("byID", c_byte * STREAM_ID_LEN),  # 流ID
        ("dwChannel", c_uint32),  # 关联的设备通道
        ("byRes", c_byte * 32)  # 保留字段
    ]

# 定义NET_DVR_MRD_SEARCH_PARAM结构体
class NET_DVR_MRD_SEARCH_PARAM(Structure):
    _fields_ = [
        ("dwSize", c_uint32),  # 结构体大小
        ("struStreamInfo", NET_DVR_STREAM_INFO),  # 监控点信息
        ("wYear", c_uint16),  # 年
        ("byMonth", c_byte),  # 月
        ("byDrawFrame", c_byte),  # 是否抽帧：0-不抽帧，1-抽帧
        ("byStreamType", c_byte),  # 码流类型：0-主码流，1-子码流
        ("byLocalOrUTC", c_byte),  # 时区：0-设备本地时区，1-UTC
        ("byRes", c_byte * 30)  # 保留字段
    ]

# 定义月历录像分布查询结果结构体
class NET_DVR_MRD_SEARCH_RESULT(Structure):
    _fields_ = [
        ("dwSize", c_uint32),            # 结构体大小
        ("byRecordDistribution", c_byte * 32),  # 录像分布
        ("byHasEventRecode", c_byte * 31),      # 事件录像分布
        ("byRes", c_byte)                # 保留字段
    ]
# 定义 NET_DVR_DIGITAL_CHANNEL_STATE 结构体
class NET_DVR_DIGITAL_CHANNEL_STATE(ctypes.Structure):
    _fields_ = [
        ("dwSize", ctypes.c_uint32),  # 结构体大小
        ("byDigitalAudioChanTalkState", ctypes.c_uint8 * MAX_CHANNUM_V30),  # 数字语音通道对讲状态
        ("byDigitalChanState", ctypes.c_uint8 * MAX_CHANNUM_V30),  # 数字通道状态
        ("byDigitalAudioChanTalkStateEx", ctypes.c_uint8 * (MAX_CHANNUM_V30 * 3)),  # 扩展数字语音通道对讲状态
        ("byDigitalChanStateEx", ctypes.c_uint8 * (MAX_CHANNUM_V30 * 3)),  # 扩展数字通道状态
        ("byRes", ctypes.c_uint8 * 64),  # 保留字段
    ]

# 定义常量
MAX_ALARMOUT_V40 = 64  # 最大触发报警输出通道数
MAX_DAYS = 7           # 一周7天
MAX_TIMESEGMENT_V30 = 8  # 每天最多8个时间段
MAX_CHANNUM_V40 = 64   # 最大通道数
# 定义NET_DVR_RGB_COLOR结构体
class NET_DVR_RGB_COLOR(ctypes.Structure):
    _fields_ = [
        ("byRed", ctypes.c_byte),   # 红色分量
        ("byGreen", ctypes.c_byte), # 绿色分量
        ("byBlue", ctypes.c_byte),  # 蓝色分量
        ("byRes", ctypes.c_byte)    # 保留字段
    ]
# 定义NET_DVR_SCHEDTIME结构体
class NET_DVR_SCHEDTIME(ctypes.Structure):
    _fields_ = [
        ("byStartHour", ctypes.c_byte),  # 开始时间：时
        ("byStartMin", ctypes.c_byte),   # 开始时间：分
        ("byStopHour", ctypes.c_byte),   # 结束时间：时
        ("byStopMin", ctypes.c_byte)     # 结束时间：分
    ]

# 定义NET_DVR_HIDEALARM_V40结构体
class NET_DVR_HIDEALARM_V40(ctypes.Structure):
    _fields_ = [
        ("dwEnableHideAlarm", ctypes.c_ulong),  # 是否启动遮挡报警
        ("wHideAlarmAreaTopLeftX", ctypes.c_ushort), # 遮挡区域的x坐标
        ("wHideAlarmAreaTopLeftY", ctypes.c_ushort), # 遮挡区域的y坐标
        ("wHideAlarmAreaWidth", ctypes.c_ushort), # 遮挡区域的宽
        ("wHideAlarmAreaHeight", ctypes.c_ushort), # 遮挡区域的高
        ("dwHandleType", ctypes.c_ulong),         # 报警处理方式
        ("dwMaxRelAlarmOutChanNum", ctypes.c_ulong), # 最大支持的触发报警输出通道数
        ("dwRelAlarmOut", ctypes.c_ulong * MAX_ALARMOUT_V40), # 触发报警输出号
        ("struAlarmTime", NET_DVR_SCHEDTIME * MAX_DAYS * MAX_TIMESEGMENT_V30), # 布防时间
        ("byRes", ctypes.c_byte * 64)             # 保留字段
    ]
# 定义 NET_DVR_MOTION_SINGLE_AREA 结构体
class NET_DVR_MOTION_SINGLE_AREA(ctypes.Structure):
    _fields_ = [
        ("byMotionScope", ctypes.c_ubyte * 64 * 96),  # 移动侦测区域（96x64数组）
        ("byMotionSensitive", ctypes.c_ubyte),       # 移动侦测灵敏度
        ("byRes", ctypes.c_ubyte * 3)                # 保留字段
    ]
# 定义 NET_DVR_DAYTIME 结构体
class NET_DVR_DAYTIME(ctypes.Structure):
    _fields_ = [
        ("byHour", ctypes.c_ubyte),        # 时，取值范围：0~24
        ("byMinute", ctypes.c_ubyte),      # 分，取值范围：0~60
        ("bySecond", ctypes.c_ubyte),      # 秒，取值范围：0~60
        ("byRes", ctypes.c_ubyte),         # 保留，置为0
        ("wMilliSecond", ctypes.c_uint16), # 毫秒，取值范围：0~1000
        ("byRes1", ctypes.c_ubyte * 2)     # 保留，置为0
    ]
# 定义 NET_DVR_SCHEDULE_DAYTIME 结构体
class NET_DVR_SCHEDULE_DAYTIME(ctypes.Structure):
    _fields_ = [
        ("struStartTime", NET_DVR_DAYTIME),  # 开始时间
        ("struStopTime", NET_DVR_DAYTIME)    # 结束时间
    ]

# 定义 NET_VCA_RECT 结构体
class NET_VCA_RECT(ctypes.Structure):
    _fields_ = [
        ("fX", ctypes.c_float),      # 边界框左上角点的X轴坐标，取值范围[0.000,1]
        ("fY", ctypes.c_float),      # 边界框左上角点的Y轴坐标，取值范围[0.000,1]
        ("fWidth", ctypes.c_float),  # 边界框的宽度，取值范围[0.000,1]
        ("fHeight", ctypes.c_float)  # 边界框的高度，取值范围[0.000,1]
    ]

# 定义 NET_DVR_DNMODE 结构体
class NET_DVR_DNMODE(ctypes.Structure):
    _fields_ = [
        ("byObjectSize", ctypes.c_ubyte),      # 占比参数，取值范围：0~100
        ("byMotionSensitive", ctypes.c_ubyte), # 移动侦测灵敏度，取值范围：1~100，0xff表示关闭
        ("byRes", ctypes.c_ubyte * 6)          # 保留字段，置为0
    ]
# 定义 NET_DVR_MOTION_MULTI_AREAPARAM 结构体
class NET_DVR_MOTION_MULTI_AREAPARAM(ctypes.Structure):
    _fields_ = [
        ("byAreaNo", ctypes.c_ubyte),          # 区域编号，从1开始
        ("byRes", ctypes.c_ubyte * 3),         # 保留字段，置为0
        ("struRect", NET_VCA_RECT),            # 单个区域的坐标信息(矩形)
        ("struDayNightDisable", NET_DVR_DNMODE),  # 关闭模式参数
        ("struDayModeParam", NET_DVR_DNMODE),  # 白天模式参数
        ("struNightModeParam", NET_DVR_DNMODE),# 夜晚模式参数
        ("byRes1", ctypes.c_ubyte * 8)         # 保留字段，置为0
    ]

#定义常量
MAX_MULTI_AREA_NUM = 24  # 最大支持设置24个区
# 定义 NET_DVR_MOTION_MULTI_AREA 结构体
class NET_DVR_MOTION_MULTI_AREA(ctypes.Structure):
    _fields_ = [
        ("byDayNightCtrl", ctypes.c_ubyte),    # 日夜控制：0-关闭，1-自动切换，2-定时切换
        ("byAllMotionSensitive", ctypes.c_ubyte),  # 全局移动侦测灵敏度，取值范围：0~5，0xff表示关闭
        ("byRes", ctypes.c_ubyte * 2),         # 保留字段，置为0
        ("struScheduleTime", NET_DVR_SCHEDULE_DAYTIME),  # 定时切换时间
        ("struMotionMultiAreaParam", NET_DVR_MOTION_MULTI_AREAPARAM * MAX_MULTI_AREA_NUM),  # 移动侦测区域参数
        ("byRes1", ctypes.c_ubyte * 60)        # 保留字段，置为0
    ]
# 定义移动侦测模式参数结构体
class NET_DVR_MOTION_MODE_PARAM(ctypes.Structure):
    _fields_ = [
        ("struMotionSingleArea", NET_DVR_MOTION_SINGLE_AREA),  # 单区域参数
        ("struMotionMultiArea", NET_DVR_MOTION_MULTI_AREA)     # 多区域参数
    ]


# 定义NET_DVR_MOTION_V40结构体
class NET_DVR_MOTION_V40(ctypes.Structure):
    _fields_ = [
        ("struMotionMode", NET_DVR_MOTION_MODE_PARAM),  # 移动侦测不同模式下区域设置
        ("byEnableHandleMotion", ctypes.c_byte),        # 是否处理移动侦测
        ("byEnableDisplay", ctypes.c_byte),             # 是否启用移动侦测高亮显示
        ("byConfigurationMode", ctypes.c_byte),         # 移动侦测模式
        ("byKeyingEnable", ctypes.c_byte),              # 是否启用键控移动侦测
        ("dwHandleType", ctypes.c_ulong),               # 处理方式
        ("dwMaxRelAlarmOutChanNum", ctypes.c_ulong),    # 最大支持的触发报警输出通道数
        ("dwRelAlarmOut", ctypes.c_ulong * MAX_ALARMOUT_V40), # 触发报警输出号
        ("struAlarmTime", NET_DVR_SCHEDTIME * MAX_DAYS * MAX_TIMESEGMENT_V30), # 布防时间
        ("dwMaxRecordChanNum", ctypes.c_ulong),         # 最大关联录像通道数
        ("dwRelRecordChan", ctypes.c_ulong * MAX_CHANNUM_V40), # 实际触发录像通道
        ("byRes", ctypes.c_byte * 128)                  # 保留字段
    ]
# 定义NET_DVR_VILOST_V40结构体
class NET_DVR_VILOST_V40(ctypes.Structure):
    _fields_ = [
        ("dwEnableVILostAlarm", ctypes.c_ulong),  # 是否启动信号丢失报警
        ("dwHandleType", ctypes.c_ulong),         # 报警处理方式
        ("dwMaxRelAlarmOutChanNum", ctypes.c_ulong), # 最大支持的触发报警输出通道数
        ("dwRelAlarmOut", ctypes.c_ulong * MAX_ALARMOUT_V40), # 触发报警输出号
        ("struAlarmTime", NET_DVR_SCHEDTIME * MAX_DAYS * MAX_TIMESEGMENT_V30), # 布防时间
        ("byVILostAlarmThreshold", ctypes.c_byte), # 信号丢失报警阈值
        ("byRes", ctypes.c_byte * 63)             # 保留字段
    ]
# 定义NET_DVR_SHELTER结构体
class NET_DVR_SHELTER(ctypes.Structure):
    _fields_ = [
        ("wHideAreaTopLeftX", ctypes.c_ushort),  # 遮挡区域的x坐标
        ("wHideAreaTopLeftY", ctypes.c_ushort),  # 遮挡区域的y坐标
        ("wHideAreaWidth", ctypes.c_ushort),     # 遮挡区域的宽
        ("wHideAreaHeight", ctypes.c_ushort)     # 遮挡区域的高
    ]
# 定义NET_DVR_COLOR结构体
class NET_DVR_COLOR(ctypes.Structure):
    _fields_ = [
        ("byBrightness", ctypes.c_byte),  # 亮度
        ("byContrast", ctypes.c_byte),    # 对比度（保留）
        ("bySaturation", ctypes.c_byte),  # 饱和度（保留）
        ("byHue", ctypes.c_byte)          # 色调（保留）
    ]    
# 定义NET_DVR_VICOLOR结构体
class NET_DVR_VICOLOR(ctypes.Structure):
    _fields_ = [
        ("struColor", NET_DVR_COLOR * MAX_TIMESEGMENT_V30),  # 图像参数
        ("struHandleTime", NET_DVR_SCHEDTIME * MAX_TIMESEGMENT_V30)  # 处理时间段
    ]
# 定义常量
NAME_LEN = 32  # 通道名称长度
MAX_SHELTERNUM = 4  # 最大隐私遮蔽区域数量
# 定义常量
MAX_DAYS = 7  # 一周的天数
MAX_TIMESEGMENT_V30 = 4  # 每天的最大时间段数
# 定义NET_DVR_PICCFG_V40结构体
class NET_DVR_PICCFG_V40(ctypes.Structure):
    _fields_ = [
        ("dwSize", ctypes.c_ulong),
        ("sChanName", ctypes.c_byte * NAME_LEN),
        ("dwVideoFormat", ctypes.c_ulong),
        ("struViColor", NET_DVR_VICOLOR),
        ("dwShowChanName", ctypes.c_ulong),
        ("wShowNameTopLeftX", ctypes.c_ushort),
        ("wShowNameTopLeftY", ctypes.c_ushort),
        ("dwEnableHide", ctypes.c_ulong),
        ("struShelter", NET_DVR_SHELTER * MAX_SHELTERNUM),
        ("dwShowOsd", ctypes.c_ulong),
        ("wOSDTopLeftX", ctypes.c_ushort),
        ("wOSDTopLeftY", ctypes.c_ushort),
        ("byOSDType", ctypes.c_byte),
        ("byDispWeek", ctypes.c_byte),
        ("byOSDAttrib", ctypes.c_byte),
        ("byHourOSDType", ctypes.c_byte),
        ("byFontSize", ctypes.c_byte),
        ("byOSDColorType", ctypes.c_byte),
        ("byAlignment", ctypes.c_byte),
        ("byOSDMilliSecondEnable", ctypes.c_byte),
        ("struVILost", NET_DVR_VILOST_V40),
        ("struAULost", NET_DVR_VILOST_V40),
        ("struMotion", NET_DVR_MOTION_V40),
        ("struHideAlarm", NET_DVR_HIDEALARM_V40),
        ("struOsdColor", NET_DVR_RGB_COLOR),
        ("dwBoundary", ctypes.c_ulong),
        ("byRes", ctypes.c_byte * 120)
    ]
# 获取当前系统环境
def GetPlatform():
    sysstr = platform.system()
    print('' + sysstr)
    if sysstr != "Windows":
        global WINDOWS_FLAG
        WINDOWS_FLAG = False

# 设置SDK初始化依赖库路径
def SetSDKInitCfg():
    # 设置HCNetSDKCom组件库和SSL库加载路径
    # print(os.getcwd())
    if WINDOWS_FLAG:
        strPath = os.getcwd().encode('gbk')
        sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()
        sdk_ComPath.sPath = strPath
        Objdll.NET_DVR_SetSDKInitCfg(2, byref(sdk_ComPath))
        Objdll.NET_DVR_SetSDKInitCfg(3, create_string_buffer(strPath + b'\libcrypto-1_1-x64.dll'))
        Objdll.NET_DVR_SetSDKInitCfg(4, create_string_buffer(strPath + b'\libssl-1_1-x64.dll'))
    else:
        strPath = os.getcwd().encode('utf-8')
        sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()
        sdk_ComPath.sPath = strPath
        Objdll.NET_DVR_SetSDKInitCfg(2, byref(sdk_ComPath))
        Objdll.NET_DVR_SetSDKInitCfg(3, create_string_buffer(strPath + b'/libcrypto.so.1.1'))
        Objdll.NET_DVR_SetSDKInitCfg(4, create_string_buffer(strPath + b'/libssl.so.1.1'))

def LoginDev(Objdll,dev_ip, dev_port, dev_user_name, dev_password):
    # 登录注册设备
    device_info = NET_DVR_DEVICEINFO_V30()
    lUserId = Objdll.NET_DVR_Login_V30(dev_ip, dev_port, dev_user_name, dev_password, byref(device_info))
    return (lUserId, device_info)

# 从 Excel 读取 NVR 信息
def read_nvr_info_from_excel(file_path):
    wb = load_workbook(file_path)
    ws = wb.active
    nvr_list = []
    for row in ws.iter_rows(min_row=2, values_only=True):  # 跳过表头
        ip, port, username, password,nvr_name = row
        nvr_list.append({
            "ip": ip,
            "port": port,
            "username": username,
            "password": password,
            "nvr_name":nvr_name
        })
    return nvr_list
# 示例调用

# 查找录像文件的函数
def find_nvr_recordings(ip, port, username, password,nvr_name):
    now=datetime.now()
    year = now.year
    month = now.month
    days_in_month = calendar.monthrange(year, month)[1]
    # 初始化DLL
    Objdll.NET_DVR_Init()
    # 启用SDK写日志
    Objdll.NET_DVR_SetLogToFile(3, bytes('./SdkLog_Python/', encoding="utf-8"), False)

    (lUserId, device_info) = LoginDev(Objdll,create_string_buffer(ip.encode('utf-8')), port, create_string_buffer(username.encode('utf-8')), create_string_buffer(password.encode('utf-8')))
    if lUserId < 0:
        err = Objdll.NET_DVR_GetLastError()
        logger.error('Login device fail, error code is: %d' % Objdll.NET_DVR_GetLastError())
        # 释放资源
        Objdll.NET_DVR_Cleanup()
        return [[ip,nvr_name,-1,0,0,0,0,0]]
    logger.info(f'登录{ip}:{port},{nvr_name}成功')
    print("Login success!")
    print("User ID:", lUserId)
    print("Device Serial Number:", bytes(device_info.sSerialNumber).decode('utf-8').strip('\x00'))

    #获取总通道数量
    total_channels = device_info.byChanNum
    total_ip_channels = device_info.byIPChanNum
    start_channel = device_info.byStartChan
    print("Total Channel Number:", total_channels)
    print("Total ip Channel Number:", total_ip_channels)
    print("Start Channel Number:", start_channel)


    # 定义函数原型
    NET_DVR_InquiryRecordTimeSpan = Objdll.NET_DVR_InquiryRecordTimeSpan
    NET_DVR_InquiryRecordTimeSpan.argtypes = [
        ctypes.c_long,  # lUserID
        ctypes.c_uint32,  # dwChannel
        ctypes.POINTER(NET_DVR_RECORD_TIME_SPAN_INQUIRY),  # lpInquiry
        ctypes.POINTER(NET_DVR_RECORD_TIME_SPAN),  # lpResult
    ]
    NET_DVR_InquiryRecordTimeSpan.restype = ctypes.c_bool  # BOOL


    
    # 定义函数原型（必须严格匹配SDK声明）
    NET_DVR_GetDVRConfig=Objdll.NET_DVR_GetDVRConfig
    Objdll.NET_DVR_GetDVRConfig.argtypes = [
        c_long,    # lUserID
        c_uint32,  # dwCommand
        c_long,    # lChannel
        c_void_p,  # lpOutBuffer
        c_uint32,  # dwOutBufferSize
        POINTER(c_uint32)  # lpBytesReturned
    ]
    Objdll.NET_DVR_GetDVRConfig.restype = c_bool  # BOOL返回值
    def get_digital_channel_state(lUserID):
        # 初始化结构体
        channel_state = NET_DVR_DIGITAL_CHANNEL_STATE()
        channel_state.dwSize = ctypes.sizeof(NET_DVR_DIGITAL_CHANNEL_STATE)

        # 调用函数
        bytes_returned = ctypes.c_uint32(0)
        success = NET_DVR_GetDVRConfig(
            lUserID,  # 用户ID
            DW_COMMAND,  # 配置命令
            0xFFFFFFFF,  # 通道号（无效时置为 0xFFFFFFFF）
            ctypes.byref(channel_state),  # 输出缓冲区
            ctypes.sizeof(NET_DVR_DIGITAL_CHANNEL_STATE),  # 缓冲区大小
            ctypes.byref(bytes_returned),  # 实际返回的数据长度
        )

        if success:
            #print("获取数字通道状态成功")
            enabled_channels = []
            # 解析数字通道状态
            #print("数字通道状态 (byDigitalChanState):")
            for i in range(total_ip_channels):
                #print(f"通道 {i + 1}: {channel_state.byDigitalChanState[i]}")
                if channel_state.byDigitalChanState[i] != 0:
                    enabled_channels.append(i + 1)  # 通道号从 1 开始
            #print("启用的数字通道:", enabled_channels)
            return enabled_channels
        else:
            #print("获取数字通道状态失败")
            raise ValueError("获取数字通道失败")
    def get_piccfg_40(lChannel):
            # 初始化结构体
            channel_state = NET_DVR_DIGITAL_CHANNEL_STATE()
            channel_state.dwSize = ctypes.sizeof(NET_DVR_DIGITAL_CHANNEL_STATE)
            # 创建输出缓冲区和返回长度变量
            out_buffer = NET_DVR_PICCFG_V40()
            dwOutBufferSize = ctypes.sizeof(NET_DVR_PICCFG_V40)
            lpBytesReturned = ctypes.c_ulong(0)

            # 调用函数
            result = Objdll.NET_DVR_GetDVRConfig(
                lUserId,
                1002,
                lChannel,
                ctypes.byref(out_buffer),
                dwOutBufferSize,
                ctypes.byref(lpBytesReturned)
            )

            # 检查调用结果
            if result:
                print("获取配置信息成功！")
                print("通道名称:", bytes(out_buffer.sChanName).decode("gb18030",errors='ignore').strip('\x00'))
                #print("视频制式:", out_buffer.dwVideoFormat)
                return bytes(out_buffer.sChanName).decode("gb18030",errors='ignore').replace('\x00','')
            else:
                print("获取配置信息失败！")
                return None
    def get_monthly_record_distribution(dwChannel,wYear,byMonth):
        
        # 定义查询条件
        search_param = NET_DVR_MRD_SEARCH_PARAM()
        search_param.dwSize = sizeof(NET_DVR_MRD_SEARCH_PARAM)
        search_param.struStreamInfo.dwSize = sizeof(NET_DVR_STREAM_INFO)
        search_param.struStreamInfo.dwChannel = dwChannel  # 通道1
        search_param.struStreamInfo.byID = (c_byte * STREAM_ID_LEN)()  # 流ID（置为空）
        search_param.struStreamInfo.byRes = (c_byte * 32)()  # 保留字段（置为0）
        search_param.wYear = wYear  # 查询年份
        search_param.byMonth = byMonth  # 查询月份
        search_param.byDrawFrame = 0  # 不抽帧
        search_param.byStreamType = 0  # 主码流
        search_param.byLocalOrUTC = 0  # 设备本地时区
        search_param.byRes = (c_byte * 30)()  # 保留字段（置为0）
        #print(search_param.struStreamInfo.byID)
        # 定义查询结果
        search_result = NET_DVR_MRD_SEARCH_RESULT()
        search_result.dwSize = sizeof(NET_DVR_MRD_SEARCH_RESULT)

        # 定义错误信息
        status_list = (c_uint32 * 1)()

        # 查询月历录像分布
        result = Objdll.NET_DVR_GetDeviceConfig(
            lUserId,                          # 用户ID
            6164,                                # 配置命令：0（月历录像分布查询）
            0,                                # 配置个数
            byref(search_param),              # 查询条件
            sizeof(search_param),             # 查询条件大小
            byref(status_list),               # 错误信息列表
            byref(search_result),             # 查询结果
            sizeof(search_result)             # 查询结果大小
        )

        if result:
            a=search_result.byRecordDistribution
            #print(value for x, value in enumerate(a))
            min_x = next((x for x, value in enumerate(a) if value == 1), None)
            max_x = next((len(a) - 1 - i for i, value in enumerate(reversed(a)) if value == 1), None)
            if min_x is None:
                #print("没有找到值为 1 的 day[x]")
                return min_x,max_x
            else:   
                #(f"{min_x+1}]   {max_x+1}")
                return min_x+1,max_x+1
        else:
            #print("Month Record Distribution Query Failed, error code:", Objdll.NET_DVR_GetLastError())
            logger.error(f"{ip} {dwChannel}获取月历录像失败,{Objdll.NET_DVR_GetLastError()}")
            return None,None
    try:
        
        
        enabled_channels=get_digital_channel_state(lUserId)
    except Exception as e:
        print(e)
        return [[ip,nvr_name,-1,0,0,0,0,e]]
        #enabled_channels=list(range(1,total_ip_channels+1))

        
    print(enabled_channels)
    results=[]
    if enabled_channels:
        logger.info(f"{ip}获取通道状态成，继续获取通道月历录像")
        for  dwChannel in enabled_channels:

            #查询录像日历
            print(f"查询通道{ip}:{dwChannel}")
            channel_name=f'{get_piccfg_40(dwChannel)}'
            
            day,today=get_monthly_record_distribution(dwChannel,year,month)
            if today is None:
                record_today=""
            else:
                record_today=f"{year}-{month}-{today}" 

            if day is None:
                record_day=""
            else:
                record_day=f"{year}-{month}-{day}" 
                
            last_year=year
            last_month=month
            while day == 1:
                if last_month == 1:
                    last_year_tmp=last_year-1
                    last_month_tmp=12
                else:
                    last_year_tmp=last_year
                    last_month_tmp=last_month-1

                day,_=get_monthly_record_distribution(dwChannel,last_year_tmp,last_month_tmp)
                if day is None:
                    record_day=f"{last_year}-{last_month}-{1}"

                elif day !=1:
                    record_day=f"{last_year_tmp}-{last_month_tmp}-{day}"

                last_year=last_year_tmp
                last_month=last_month_tmp
            print(f"record_day:{record_day},record_today:{record_today}")
            start_day = ''
            end_day =''
            # 初始化查询条件结构体
            inquiry = NET_DVR_RECORD_TIME_SPAN_INQUIRY()
            inquiry.dwSize = ctypes.sizeof(NET_DVR_RECORD_TIME_SPAN_INQUIRY)
            inquiry.byType = 0  # 查询正常音视频录像
            inquiry.byRes = (ctypes.c_uint8 * 63)()  # 保留字段初始化为 0

            # 初始化结果结构体
            result = NET_DVR_RECORD_TIME_SPAN()
            result.dwSize = ctypes.sizeof(NET_DVR_RECORD_TIME_SPAN)

            # 调用函数
            success = NET_DVR_InquiryRecordTimeSpan(lUserId, dwChannel, ctypes.byref(inquiry), ctypes.byref(result))
                
            if success:
                print(f"通道{dwChannel}查询成功")

                start_day = f"{result.strBeginTime.dwYear}-{result.strBeginTime.dwMonth}-{result.strBeginTime.dwDay} {result.strBeginTime.dwHour}:{result.strBeginTime.dwMinute}:{result.strBeginTime.dwSecond}"
                end_day = f"{result.strEndTime.dwYear}-{result.strEndTime.dwMonth}-{result.strEndTime.dwDay} {result.strEndTime.dwHour}:{result.strEndTime.dwMinute}:{result.strEndTime.dwSecond}"
                
                    
            else:
                print("查询失败")
            results.append([
                    ip,
                    nvr_name,
                    dwChannel,
                    channel_name,
                    start_day,
                    end_day,
                    record_day,
                    record_today,
                    (datetime.strptime(record_today, "%Y-%m-%d") - datetime.strptime(record_day, "%Y-%m-%d")).days if record_day and record_today else 0
                    ])

    # 注销登录
    Objdll.NET_DVR_Logout(lUserId)

    # 清理 SDK
    Objdll.NET_DVR_Cleanup()
    #print(results)
    return results
    # 登出设备
    #Objdll.NET_DVR_Logout(lUserId)

    # 释放资源
    #Objdll.NET_DVR_Cleanup()
def main():
    
    """
    主函数，用于执行录像文件查询任务
    包括初始化时间、创建Excel文件、多线程查询录像信息、保存结果等步骤
    """
    # 获取当前时间并格式化为字符串
    start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    # 记录日志，输出包含当前日期的Excel文件名
    logger.info(f"录像文件查询结果{datetime.now().strftime("%Y-%m-%d")}.xlsx")
    # 构建输出文件的完整路径，文件名包含精确到秒的时间戳
    output_filepath = os.path.join(basepath , f"录像文件查询结果{datetime.now().strftime("%Y-%m-%d-%H-%M-%S")}.xlsx")
    # 创建 Excel 工作簿和工作表
    result_wb = Workbook()
    result_ws = result_wb.active
    result_ws.title = "录像文件"
    # 添加Excel表头
    result_ws.append(["NVR IP", "NVR 名称","通道号","通道名称" ,"开始时间", "结束时间","最早录像日","最后录像日","录像天数"])
    logger.info("开始检测")
    with ThreadPoolExecutor(max_workers=30) as executor:  # 最大线程数设置为 5
        futures = []
        for nvr in nvr_list:
            futures.append(executor.submit(
                find_nvr_recordings,
                nvr["ip"], nvr["port"], nvr["username"], nvr["password"],nvr['nvr_name']
            ))

        # 等待所有任务完成并写入结果
        for future in as_completed(futures):
            results = future.result()
            for result in results:
                #a=result[0].decode('utf-8')+result[1:]
                result_ws.append(result)
    # 保存 Excel 文件
    try:
        result_wb.save(output_filepath)
        logger.info(f"查询结果已保存到 {output_filepath}，检测结束")
    except Exception as e:
        logger.error(f"查询结果保存失败,原因{e}，检测结束")
    end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"开始时间:{start_time}----结束时间:{end_time}")
    #print(f"查询结果已保存到 {output_filepath}")


if __name__ == '__main__':
    # 从 Excel 读取 NVR 信息
    #start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    basepath = os.getcwd()
    nvr_list = read_nvr_info_from_excel("nvr_info.xlsx")

    # 获取系统平台
    GetPlatform()

    # 加载库,先加载依赖库
    if WINDOWS_FLAG:
        os.chdir(r'./lib/win')
        Objdll = ctypes.CDLL(r'./HCNetSDK.dll')  # 加载网络库
        #Playctrldll = ctypes.CDLL(r'./PlayCtrl.dll')  # 加载播放库
    else:
        os.chdir(r'./lib/linux')
        Objdll = cdll.LoadLibrary(r'./libhcnetsdk.so')
        #Playctrldll = cdll.LoadLibrary(r'./libPlayCtrl.so')

    SetSDKInitCfg()  # 设置组件库和SSL库加载路径
    main()

